1 Carga de paquetes

Son muchos los paquetes empleados en estos análisis. Puedes consultar en el ChatGPT qué hace cada uno. Considera un aspecto también importante: algunas funciones escritas por mí se cargan con source_url y source; dentro de algunas de dichas funciones, también se cargan paquetes adicionales.

library(vegan)
library(sf)
library(tidyverse)
library(tmap)
library(kableExtra)
library(broom)
library(cluster)
library(gclus)
library(pvclust)
library(foreach)
library(leaps)
library(caret)
library(RColorBrewer)
library(indicspecies)
library(dendextend)
library(adespatial)
library(SpadeR)
library(iNEXT)
library(GGally)
library(vegetarian)
library(leaflet)
library(leaflet.extras)
library(readxl)
r <- 'R/'
gh_content <- 'https://raw.githubusercontent.com/'
gh_zonal_stats <- paste0(gh_content,
                         'geofis/zonal-statistics/0b2e95aaee87bf326cf132d28f4bd15220bb4ec7/out/')
repo_analisis <- 'biogeografia-master/scripts-de-analisis-BCI/master'
repo_sem202202 <- 'biogeografia-202202/material-de-apoyo/master/practicas/'
repo_sem202302 <- 'biogeografia-202302/practicas/main/'
devtools::source_url(paste0(gh_content, repo_analisis, '/biodata/funciones.R'))
devtools::source_url(paste0(gh_content, repo_sem202202, 'train.R'))
devtools::source_url(paste0(gh_content, repo_sem202202, 'funciones.R'))
fuentes_manuscrito <- 'fuentes/manuscrito/'
source(paste0(gh_content, repo_sem202302, r, 'funciones.R'))
umbral_alfa <- 0.05

2 Leer y preparar datos

2.1 Formularios de Ramona Geraldo Muñoz

nombre_odk <- estudiantes_todos_datos %>% 
  filter(grepl(params$estudiante, `Nombres y apellidos compatible Params`)) %>%
  pull(odk_user)
mis_forms_campo <- odk_campo_form_usuario %>% 
  filter(grepl(nombre_odk, usuario_ODK)) %>% 
  inner_join(odk_campo) %>% 
  select(KEY, usuario_ODK, hexagono, Latitude, Longitude,
         Altitude, Accuracy, fechahora, llovio_ultima_hora,
         foto, responsable, otra_persona, observaciones_finales)
mis_forms_id <- odk_id_form_usuario %>% 
  filter(usuario_ODK == nombre_odk) %>% 
  inner_join(odk_id) %>% 
  select(-SubmissionDate, -`meta-instanceID`)

2.2 Mapa

mis_forms_campo_sf <- mis_forms_campo %>% st_as_sf(coords = c('Longitude', 'Latitude'))
leaflet(mis_forms_campo_sf) %>%
  addCircleMarkers(
    radius = 8, popup = ~ paste0(hexagono), stroke = T,
    weight = 1, fillColor = 'white', color = 'black', fillOpacity = 1,
    label = ~ hexagono,
    labelOptions = labelOptions(
      noHide = TRUE, direction = 'auto', offset = c(10, 0), textOnly = T,
      style = list('color' = 'black', 'font-weight' = 'bold',
                   'font-size' = '14px'))) %>%
  addTiles(group = 'OSM') %>%
  addProviderTiles("Esri.NatGeoWorldMap", group="ESRI Mapa") %>%
  addProviderTiles("Esri.WorldImagery", group="ESRI Imagen") %>%
  addProviderTiles("CartoDB.Positron", group= "CartoDB") %>%
  addLayersControl(
    baseGroups = c("CartoDB", "ESRI Imagen", "OSM", "ESRI Mapa"),
    position = 'bottomright',
    options = layersControlOptions(collapsed = FALSE)) %>% 
  addFullscreenControl()

2.3 Fotos

1696774974519.jpg

1696778100963.jpg

1696781165920.jpg

1696783080137.jpg

1696789430873.jpg

1696792839428.jpg

1697144993550.jpg

1697146692584.jpg

1697202292627.jpg

1697203809110.jpg

1697205198540.jpg

1697207336467.jpg

1697209207494.jpg

1697217561436.jpg

1697221868094.jpg

2.4 Generar matriz de comunidad y ambiental, prepararlas para cargarlas

3 Análisis exploratorio de datos (AED)

3.1 Cargar la matriz de comunidad

spp_odk <- read_excel(
  path = 'odk/biogeografia_hormigas_202302_identificacion.xlsx', sheet = 2) %>% 
  select(list_name, name, label) %>% 
  filter(list_name == 'especieid')
mc <- mis_forms_id %>%
  select(codigo_hexagono_elegido, codigo_hexagono_otro,
         especieid, especieidotra) %>% 
  mutate(hexagono = coalesce(codigo_hexagono_elegido, codigo_hexagono_otro)) %>% 
  select(-matches('codigo_hexagono_.*')) %>% 
  mutate(especieid_todas = case_when(
    !is.na(especieidotra) ~ paste(especieid, especieidotra, sep = " "),
    TRUE ~ as.character(especieid))) %>% 
  separate_rows(especieid) %>%
  distinct() %>% 
  left_join(spp_odk, by = c("especieid" = "name")) %>% 
  select(hexagono, especie = label) %>% 
  filter(especie != 'reina(s)') %>%
  mutate(presencia = 1) %>% 
  arrange(especie) %>% 
  pivot_wider(names_from = especie, values_from = presencia, values_fill = 0) %>% 
  arrange(hexagono)
write.csv(mc,
          paste0(fuentes_manuscrito, 'matriz-comunidad-', params$estudiante, '.csv'),
          row.names = F)
mc <- read.csv(
  file = paste0(fuentes_manuscrito, 'matriz-comunidad-', params$estudiante, '.csv'),
  row.names = 'hexagono', check.names = F)
mc %>% estilo_kable(
  titulo = 'Matriz de comunidad',
  nombres_filas = T, alinear = 'r')
TABLA 3.1: Matriz de comunidad
Brachymyrmex heeri Brachymyrmex sp. Cardiocondyla emeryi Crematogaster steinheili Cyphomyrmex rimosus Dorymyrmex antillana Monomorium floricola Monomorium pharaonis Paratrechina longicornis Pheidole subarmata Pseudomyrmex cubaensis Solenopsis geminata Tapinoma melanocephalum Tetramorium caldarium Tetramorium lanuginosum Wasmannia auropunctata
H0098 1 0 0 0 0 1 0 1 1 0 0 0 1 0 0 0
H0192 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
H0333 0 0 0 0 1 0 0 0 1 1 0 0 0 0 1 0
H0375 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0
H0488 1 0 0 0 0 1 0 0 1 1 1 0 0 0 1 0
H0717 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
H0781 1 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0
H0796 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0
H0815 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 1
H0867 1 0 0 0 0 0 0 0 1 1 0 1 0 1 1 0
H1043 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
H1219 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
H1318 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
H1337 1 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0
data.frame(Especies = sort(names(mc))) %>%
  estilo_kable(titulo = 'Lista de especies', cubre_anchura = F, alinear = 'c') %>% 
  column_spec(column = 1, width = "15em")
TABLA 3.2: Lista de especies
Especies
Brachymyrmex heeri
Brachymyrmex sp.
Cardiocondyla emeryi
Crematogaster steinheili
Cyphomyrmex rimosus
Dorymyrmex antillana
Monomorium floricola
Monomorium pharaonis
Paratrechina longicornis
Pheidole subarmata
Pseudomyrmex cubaensis
Solenopsis geminata
Tapinoma melanocephalum
Tetramorium caldarium
Tetramorium lanuginosum
Wasmannia auropunctata
data.frame(`Número de sitios donde fue reportada la especie` = sort(colSums(mc), decreasing = T),
           check.names = F) %>%
  rownames_to_column('Especie') %>% 
  estilo_kable(
    titulo = 'Número de sitios en los que está presente cada especie (orden descendente por número de sitios)', 
    nombres_filas = F, alinear = 'cr')
TABLA 3.3: Número de sitios en los que está presente cada especie (orden descendente por número de sitios)
Especie Número de sitios donde fue reportada la especie
Brachymyrmex heeri 10
Dorymyrmex antillana 10
Solenopsis geminata 6
Tetramorium lanuginosum 5
Paratrechina longicornis 4
Pheidole subarmata 4
Monomorium floricola 2
Brachymyrmex sp. 1
Cardiocondyla emeryi 1
Crematogaster steinheili 1
Cyphomyrmex rimosus 1
Monomorium pharaonis 1
Pseudomyrmex cubaensis 1
Tapinoma melanocephalum 1
Tetramorium caldarium 1
Wasmannia auropunctata 1
data.frame(`Riqueza por sitios` = rowSums(mc),
           check.names = F) %>%  rownames_to_column('Sitio') %>% 
  arrange(desc(`Riqueza por sitios`)) %>% 
  estilo_kable(
    titulo = 'Riqueza por sitios (orden descendente por riqueza)', 
    nombres_filas = F, alinear = 'cr')
TABLA 3.4: Riqueza por sitios (orden descendente por riqueza)
Sitio Riqueza por sitios
H0488 6
H0867 6
H1337 6
H0098 5
H0815 5
H0333 4
H0375 4
H0781 4
H0796 3
H1043 2
H1318 2
H0192 1
H0717 1
H1219 1

La matriz de comunidad analizada se compone de 14 sitios y 16 especies, donde el/los sitio/s más ricos es/son H0488, H0867 y H1337. La/s especie/s más común/es es/son Brachymyrmex heeri y Dorymyrmex antillana y la/s más rara/s es/son Brachymyrmex sp., Cardiocondyla emeryi, Crematogaster steinheili, Cyphomyrmex rimosus, Monomorium pharaonis, Pseudomyrmex cubaensis, Tapinoma melanocephalum, Tetramorium caldarium y Wasmannia auropunctata. El siguiente gráfico de mosaicos muestra la distribución de las especies según sitios.

grafico_mosaico <- crear_grafico_mosaico_de_mc(mc, tam_rotulo = 12) + xlab('Sitios') + ylab('Especie')
grafico_mosaico
Distribución de las especies según sitios

FIGURA 3.1: Distribución de las especies según sitios

3.2 Transformar la matriz de comunidad

Este paso es importante, lo explico aquí

mc_t <- decostand(mc, 'hellinger') #Hellinger, funciona con datos de presencia/ausencia
mc_t %>% estilo_kable(titulo = 'Matriz de comunidad transformada',
                      nombres_filas = T, alinear = 'r')
TABLA 3.5: Matriz de comunidad transformada
Brachymyrmex heeri Brachymyrmex sp. Cardiocondyla emeryi Crematogaster steinheili Cyphomyrmex rimosus Dorymyrmex antillana Monomorium floricola Monomorium pharaonis Paratrechina longicornis Pheidole subarmata Pseudomyrmex cubaensis Solenopsis geminata Tapinoma melanocephalum Tetramorium caldarium Tetramorium lanuginosum Wasmannia auropunctata
H0098 0.45 0.00 0.00 0.0 0.0 0.45 0.00 0.45 0.45 0.00 0.00 0.00 0.45 0.00 0.00 0.00
H0192 0.00 0.00 0.00 0.0 0.0 0.00 0.00 0.00 0.00 0.00 0.00 1.00 0.00 0.00 0.00 0.00
H0333 0.00 0.00 0.00 0.0 0.5 0.00 0.00 0.00 0.50 0.50 0.00 0.00 0.00 0.00 0.50 0.00
H0375 0.50 0.00 0.00 0.0 0.0 0.50 0.50 0.00 0.00 0.00 0.00 0.50 0.00 0.00 0.00 0.00
H0488 0.41 0.00 0.00 0.0 0.0 0.41 0.00 0.00 0.41 0.41 0.41 0.00 0.00 0.00 0.41 0.00
H0717 1.00 0.00 0.00 0.0 0.0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
H0781 0.50 0.00 0.00 0.5 0.0 0.50 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.50 0.00
H0796 0.58 0.00 0.00 0.0 0.0 0.58 0.00 0.00 0.00 0.00 0.00 0.58 0.00 0.00 0.00 0.00
H0815 0.00 0.45 0.00 0.0 0.0 0.45 0.00 0.00 0.00 0.45 0.00 0.45 0.00 0.00 0.00 0.45
H0867 0.41 0.00 0.00 0.0 0.0 0.00 0.00 0.00 0.41 0.41 0.00 0.41 0.00 0.41 0.41 0.00
H1043 0.71 0.00 0.00 0.0 0.0 0.71 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
H1219 0.00 0.00 0.00 0.0 0.0 1.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
H1318 0.71 0.00 0.00 0.0 0.0 0.71 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
H1337 0.41 0.00 0.41 0.0 0.0 0.41 0.41 0.00 0.00 0.00 0.00 0.41 0.00 0.00 0.41 0.00
# Otras transformaciones posibles con datos de presencia/ausencia
# mc_t <- decostand(mc, 'normalize') #Chord
# mc_t <- decostand(log1p(mc), 'normalize') #Chord
# mc_t <- decostand(mc, 'chi.square') #Chi-square

3.3 Cargar la matriz ambiental

fuente_env_sf <- st_read('data/h3-res-12-no-edificios-3-grupos.gpkg') %>%
  rename(hexagono = indice_propio)
## Reading layer `h3-res-12-no-edificios-3-grupos' from data source 
##   `/home/jr/Documentos/clases_UASD/202302/repo-gh/geo131/manuscrito/data/h3-res-12-no-edificios-3-grupos.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 1490 features and 7 fields
## Geometry type: POLYGON
## Dimension:     XY
## Bounding box:  xmin: 402627 ymin: 2041098 xmax: 403677.5 ymax: 2041889
## Projected CRS: WGS 84 / UTM zone 19N
env <- fuente_env_sf %>%
  st_drop_geometry() %>% 
  select(-index, -grupo) %>% 
  filter(hexagono %in% rownames(mc)) %>% 
  column_to_rownames('hexagono')
env %>% estilo_kable(titulo = 'Matriz ambiental', nombres_filas = T, alinear = 'r')
TABLA 3.6: Matriz ambiental
porc_SUEL porc_DOSE porc_CONS porc_EDIF
H0098 79.14 0.00 20.86 0.00
H0192 100.00 0.00 0.00 0.00
H0333 0.00 99.41 0.59 0.00
H0375 51.80 0.00 37.55 10.65
H0488 7.01 74.99 18.00 0.00
H0717 0.00 8.77 65.72 25.50
H0781 0.00 56.91 43.09 0.00
H0796 1.93 38.40 59.67 0.00
H0815 40.28 59.72 0.00 0.00
H0867 0.00 74.73 25.27 0.00
H1043 0.00 0.00 100.00 0.00
H1219 34.83 2.61 62.55 0.00
H1318 63.78 0.00 36.22 0.00
H1337 64.66 35.34 0.00 0.00

La matriz ambiental se compone de 4 variables de tipo numérico, conteniendo el valor de cada variable para cada uno de los 14 sitios. La siguiente tabla y el gráfico muestran un resumen de los estadísticos básicos de la matriz ambiental.

estad_basicos <- env %>%
  pivot_longer(everything(), names_to = "Variable", values_to = "Valor") %>%
  group_by(Variable) %>%
  summarise(
    Media = mean(Valor),
    Mediana = median(Valor),
    `Desv. Estándar` = sd(Valor),
    Varianza = var(Valor),
    `Error Estándar` = sd(Valor) / sqrt(length(Valor)))
estad_basicos %>% estilo_kable(titulo = 'Matriz ambiental', nombres_filas = F, alinear = 'crrrr')
TABLA 3.7: Matriz ambiental
Variable Media Mediana Desv. Estándar Varianza Error Estándar
porc_CONS 33.54 30.75 30.41 924.80 8.13
porc_DOSE 32.21 22.06 35.28 1244.72 9.43
porc_EDIF 2.58 0.00 7.18 51.57 1.92
porc_SUEL 31.67 20.92 35.12 1233.76 9.39
env %>%
  pivot_longer(everything(), names_to = 'Variable', values_to = 'Valor') %>% 
  group_by(Variable) %>% 
  ggplot() +
  aes(x = Variable, y = Valor, color = Variable, fill = Variable) + 
  # geom_boxplot(lwd = 0.2) + 
  geom_violin(alpha = 0.2, width = 0.8, color = "transparent") +
  geom_jitter(alpha = 0.6, size = 2, height = 0, width = 0.1) +
  geom_boxplot(alpha = 0, width = 0.3, color = "#808080") +
        scale_fill_brewer(palette = 'Set1') +
        theme_bw() +
        theme(legend.position="none")

Las medias calculadas de las variables porc_SUEL, porc_DOSE, porc_CONS y porc_EDIF son, respectivamente, las siguientes: 31.67, 32.21, 33.54 y 2.58. La variable que con la media más alta fue porc_CONS (33.54), y la más baja la obtuvo la variable porc_EDIF (2.58). Por otra parte, la mitad de los sitios midieron menos de 20.92, 22.06, 30.75 y 0, para cada una de las variables porc_CONS, porc_DOSE, porc_EDIF y porc_SUEL, respectivamente. Finlamente, la variable con mayor dispersión fue porc_DOSE y la de menor dispersión fue porc_EDIF.

Una verificación importante que debe realizarse es si las matrices de comunidad y ambiental tienen el mismo numero de filas y si las filas se encuentran en el mismo orden (e.g. consistencia entre matrices, donde cada fila en la matriz de comunidad se refiere al mismo sitio en la ambiental, y viceversa). Esto se puede comprobar por medio de los nombres de columnas y, en este caso, tras realizar la correspondiente comprobación, esta condición se cumple, por lo que podemos continuar adelante con los siguientes análisis

A continuación, realizaré análisis de agrupamiento, ordenación y diversidad, basándome en las indicaciones de Borcard, Gillet, y Legendre (2018), reaprovechando el código contenido en Martínez Batlle (2020).

4 Análisis de agrupamiento

A continuación, el análisis de agrupamiento propiamente. La parte más importante es generar un árbol, a partir de una matriz de distancias, que haga sentido desde el punto de vista de la comunidad y la distribución de las especies. Primero cargaré paquetes específicos de esta técnica y generaré la matriz de distancias.

mc_d <- vegdist(mc_t, "euc")

4.1 Generación de árboles

A continuación, generaré árboles usando distintos métodos. Explico detalladamente estas técnicas en el repo, y en los vídeos (13 a 16) de la lista mencionada arriba “Ecología Numérica con R” de mi canal.

lista_cl <- list(
        cl_single = hclust(mc_d, method = 'single'),
        cl_complete = hclust(mc_d, method = 'complete'),
        cl_upgma = hclust(mc_d, method = 'average'),
        cl_ward = hclust(mc_d, method = 'ward.D2')
)
par(mfrow = c(2,2))
invisible(map(names(lista_cl), function(x) plot(lista_cl[[x]], main = paste0(x, '\n(árbol de evaluación)'), hang = -1)))

par(mfrow = c(1,1))

A continuación, calcularé la distancia y la correlación cofenéticas; esta última, la correlación cofenética,se utiliza como criterio flexible para elegir el método de agrupamiento idóneo, pero no debe usarse de manera estricta. Se supone que el método con la mayor correlación cofenética explica mejor el agrupamiento de la comunidad. Si quieres comprender mejor esta técnica, consulta el vídeo que te referí en el párrafo anterior, así como los libros de referencia. Normalmente, el método UPGMA obtiene la mayor correlación cofenética, pero esto se debe a que su procedimiento de obtención maximiza precisamente dicha métrica. No es recomendable conservar un único método de agrupamiento, normalmente es bueno usar al menos dos. Ward es muchas veces recomendado como método de contraste, por basarse en procedimientos de cálculo muy distintos a los de UPGMA.

map_df(lista_cl, function(x) {
        coph_d <- cophenetic(x)
        corr <- cor(mc_d, coph_d)
        return(corr)
}) %>% t() %>% as.data.frame() %>%
  rownames_to_column %>%
  mutate(rowname = gsub('cl_', '', rowname)) %>% 
  setNames(c('Método de agrupamiento', 'Correlación cofenética')) %>%
  estilo_kable()
TABLA 4.1:
Método de agrupamiento Correlación cofenética
single 0.73
complete 0.73
upgma 0.80
ward 0.62

4.2 Anchura de siluetas

Ahora, calcularé las anchuras de silueta, una métrica que ayuda a determinar en cuántos grupos se organiza la comunidad; las anchuras de silueta no deben usarse como método estricto, y sólo debe usarse de forma flexible para informarnos sobre el número máximo de grupos posibles. Considera las siguientes reglas:

  • El número ideal es 3 grupos, de 4 a 5 grupos es aceptable, 6 o más grupos se considera difícil de interpretar, o es un resultado poco útil; 1 grupo es un resultado sin sentido.
  • Si obtienes distintos grupos, pero uno o varios están compuestos por un único sitio, observa qué ocurre en ese sitio, pues es probable que contenga especie raras sólo presentes en él. En este caso, es recomendable explorar dos alternativas para evitar el grupo formado por un único sitio: ver qué ocurre usando distintos métodos o elegir cortar el árbol en un número de grupos menor.

4.2.1 Anchuras de siluetas para método UPGMA

# UPGMA
anch_sil_upgma <- calcular_anchuras_siluetas(
        mc_orig = mc, 
        distancias = mc_d, 
        cluster = lista_cl$cl_upgma)
u_dend_reord <- reorder.hclust(lista_cl$cl_upgma, mc_d)
plot(u_dend_reord, hang = -1, main = 'Método UPGMA\n(árbol de evaluación)')
rect.hclust(
        tree = u_dend_reord,
        k = anch_sil_upgma$n_grupos_optimo)

resultado_evaluacion_upgma <- evaluar_arbol(u_dend_reord, anch_sil_upgma$n_grupos_optimo)

Tras cortar el árbol, la evaluación practicada concluyó lo siguiente: “Árbol no recomendado para usarse por producir grupos compuestos por dos elementos o menos”

4.2.2 Anchuras de siluetas para método Ward

# Ward
anch_sil_ward <- calcular_anchuras_siluetas(
        mc_orig = mc, 
        distancias = mc_d, 
        cluster = lista_cl$cl_ward)
w_dend_reord <- reorder.hclust(lista_cl$cl_ward, mc_d)
plot(w_dend_reord, hang = -1, main = 'Método Ward\n(árbol de evaluación)')
rect.hclust(
        tree = w_dend_reord,
        k = anch_sil_ward$n_grupos_optimo)

resultado_evaluacion_ward <- evaluar_arbol(w_dend_reord, anch_sil_ward$n_grupos_optimo)

Tras cortar el árbol, la evaluación practicada concluyó lo siguiente: “Árbol útil para análisis posteriores, siempre que se corte en 3 grupos”.

4.3 Remuestreo por bootstrap multiescalar

Una forma alterna de evaluar árboles consiste en usar el remuestreo por bootstrap multiescalar. No me interesa que profundices en ella, sólo presentártela como técnica probabilística para evaluar árboles generados por métodos determinísticos. La técnica es documentada en Borcard, Gillet, y Legendre (2018), de la cual puedes un resumen en este cuaderno y en este vídeo (minuto 51:33). El remuestreo por bootstrap multiescalar valida la robustez de los análisis de agrupamiento tomando múltiples muestras aleatorias de los datos en diferentes tamaños. Este proceso determina qué grupos son consistentemente identificados como clústeres, generando valores de probabilidad aproximadamente insesgados (AU) que son considerados más fiables que las probabilidades de bootstrap tradicionales (BP). Esta técnica ayuda a identificar y confirmar patrones robustos en los datos.

Lo aplicaré primero al árbol generado por el método UPGMA.

# UPGMA
# if(interactive()) dev.new()
cl_pvclust_upgma <-
        pvclust(t(mc_t),
                method.hclust = "average",
                method.dist = "euc",
                iseed = 99, # Resultado reproducible
                parallel = TRUE, quiet = TRUE)
# Añadir los valores de p
plot(cl_pvclust_upgma, hang = -1, main = 'Método UPGMA bootstrap\n(árbol de evaluación)')
# Añadir rectángulos a los grupos significativos
lines(cl_pvclust_upgma)
pvrect(cl_pvclust_upgma, alpha = 0.90, border = 4)

Lo aplicaré también al árbol generado por el método Ward.

# Ward
# if(interactive()) dev.new()
cl_pvclust_ward <-
        pvclust(t(mc_t),
                method.hclust = "ward.D2",
                method.dist = "euc",
                iseed = 99, # Resultado reproducible
                parallel = TRUE, quiet = TRUE)
# Añadir los valores de p
plot(cl_pvclust_ward, hang = -1, main = 'Método Ward bootstrap\n(árbol de evaluación)')
# Añadir rectángulos a los grupos significativos
lines(cl_pvclust_ward)
pvrect(cl_pvclust_ward, alpha = 0.91, border = 4)

4.4 Conclusión sobre selección de método de agrupamiento y número de grupos

Basado en lo anterior, elegiré un método de agrupamiento y un número de grupos, y lo exportaré a un archivo que posteriormente podré reaprovechar. La lógica empleada para elegir método de agrupamiento y número de grupos, es la siguiente: si el árbol generado por el método UPGMA no es recomendable (por tener grupos formados 2 o menos elementos), pero Ward sí, se usar el árbol generado por el método Ward y el número de grupos idóneo sugerido por la anchura de silueta. Si UPGMA es recomendable pero Ward no lo es, se usar el árbol generado por el método UPGMA, cortado en el número de grupos sugerido por la anchura de siluetas. Si ambos métodos son recomendables y sugieren el mismo número de grupos, se opta por el arbol generado por el método Ward. Si ambos métodos son recomendables pero sugieren un número diferente de grupos, se elige el método que sugiere menos grupos. Finalmente, si ambos métodos, UPGMA y Ward, resultan ser poco idóneos porque generan grupos muy pequeños (dos o menos elementos), se opta, como último recurso, por elegir el árbol generado por el método Ward cortado en 3 grupos.

grupos_seleccionados <- seleccionar_y_cortar_arbol(
  arbol_upgma = lista_cl$cl_upgma, arbol_ward = lista_cl$cl_ward,
  resultado_evaluacion_upgma = resultado_evaluacion_upgma,
  resultado_evaluacion_ward = resultado_evaluacion_ward)
saveRDS(grupos_seleccionados$resultado,
        paste0(fuentes_manuscrito, 'grupos_seleccionados-', params$estudiante,'.RDS'))

El árbol generado por el método UPGMA produce grupos compuestos por dos elementos o menos. Usamos el árbol generado por el método Ward cortado en 3 grupos . El árbol resultante se muestra a continuación:

# Convierte el hclust en dendrograma
dend <- as.dendrogram(grupos_seleccionados$arbol)

# Corta y colorea el dendrograma en k grupos
dend_colored <- color_branches(dend, k=grupos_seleccionados$k)

# Etiqueta los grupos
labels_colors <- labels_colors(dend_colored)
labels(dend_colored) <- paste0(labels(dend_colored), " (",
                               grupos_seleccionados$resultado[grupos_seleccionados$arbol$order],
                               ")")

# Grafica el dendrograma
# par(mar = c(3, 4, 4, 2) + 0.1) # Ajusta los márgenes
plot(
  dend_colored,
  main=paste(
    'Árbol seleccionado\nMétodo',
    grupos_seleccionados$metodo,
    'cortado en',
    grupos_seleccionados$k, 'grupos'),
  xlab = 'Sitios (grupo de pertenencia)')

4.5 Grupos (clústers), variables ambientales

Apliquemos el análisis de agrupamiento a la matriz ambiental. La clave en este punto es que, si la matriz ambiental presenta patrones parecidos a los de la matriz de comunidad, significa que el agrupamiento utilizado hace sentido entre ambos conjuntos de datos (comunidad y hábitat) de forma consistente. Si ambos conjuntos de datos son consistentes, significa que existe algún grado de asociación, aunque sea sólo una mera asociación estadística.

Agrupar los sitios de muestreo de la matriz ambiental según los grupos previamente definidos.

env_grupos <- env %>%
    rownames_to_column('sitios_de_muestreo') %>% 
    mutate(grupos = as.factor(grupos_seleccionados$resultado)) %>%
    pivot_longer(-c(grupos, sitios_de_muestreo), names_to = "variable", values_to = "valor")

Evaluar efectos entre los grupos (“diferencias significativas”). Se utilizan las pruebas estadísticas ANOVA (evalúa homongeneidad de medias) y Kruskal-Wallis (evalúa homogeneidad de medianas). Las tablas están ordenadas en orden ascendente por la columna p_valor_a, que son los p-valores de la prueba ANOVA.

env_grupos_ak <- env_grupos %>%
  group_by(variable) %>%
  summarise(
    p_valor_a = tryCatch(oneway.test(valor ~ grupos)$p.value, error = function(e) NA),
    p_valor_k = tryCatch(kruskal.test(valor ~ grupos)$p.value, error = function(e) NA)
    ) %>%
  arrange(p_valor_a)
env_grupos_ak %>% estilo_kable(alinear = 'crr')
TABLA 4.2:
variable p_valor_a p_valor_k
porc_DOSE 0.00 0.03
porc_SUEL 0.04 0.13
porc_CONS 0.06 0.07
porc_EDIF NaN 0.75

Explora tus resultados.

env_grupos %>% 
        group_by(variable) %>% 
        ggplot() + aes(x = grupos, y = valor, group = grupos, fill = grupos) + 
        geom_boxplot(lwd = 0.2) + 
        scale_fill_brewer(palette = 'Set1') +
        theme_bw() +
        theme(legend.position="none") +
        facet_wrap(~ variable, scales = 'free_y', ncol = 8)

El objetivo de adjuntarle, a la matriz ambiental, el vector de agrupamiento generado a partir de datos de comunidad, consiste en caracterizar ambientalmente los hábitats de los subgrupos diferenciados según su composición. Observa los resultados de las pruebas estadísticas, de los diagramas de caja, y explora tus resultados:

4.6 Especies con preferencia/fidelidad con grupos (clústers)

Análisis de preferencia/fidelidad de especies con grupos (clusters), mediante el coeficiente de correlación biserial puntual (phi).

set.seed(9999)
phi <- multipatt(
  mc,
  grupos_seleccionados$resultado,
  func = "r.g",
  max.order = 1,
  control = how(nperm = 999))
summary(phi)

 Multilevel pattern analysis
 ---------------------------

 Association function: r.g
 Significance level (alpha): 0.05

 Total number of species: 16
 Selected number of species: 4 
 Number of species associated to 1 group: 4 
 Number of species associated to 2 groups: 0 

 List of species associated to each combination: 

 Group B  #sps.  1 
                     stat p.value   
Solenopsis geminata 0.791   0.003 **

 Group C  #sps.  3 
                          stat p.value   
Paratrechina longicornis 0.886   0.009 **
Pheidole subarmata       0.866   0.015 * 
Tetramorium lanuginosum  0.773   0.036 * 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 

Tabla de especies que presentaron asociación con grupos por medio de phi, usando umbral de significancia (umbral_alfa).

tabla_phi_sign <- phi$sign
tabla_phi_sign_alfa <- tabla_phi_sign[phi$sign$p.value < umbral_alfa, ]
data.frame(
  `Nombre de especie` = rownames(tabla_phi_sign_alfa),
  `P-valor` = tabla_phi_sign_alfa$p.value,
  `Grupo de asociación` = gsub('s\\.', '', names(tabla_phi_sign_alfa)[tabla_phi_sign_alfa$index]),
  check.names = F) %>%
  arrange(`Nombre de especie`) %>% 
  estilo_kable(alinear = 'crr')
TABLA 4.3:
Nombre de especie P-valor Grupo de asociación
Paratrechina longicornis 0.01 C
Pheidole subarmata 0.01 C
Solenopsis geminata 0.00 B
Tetramorium lanuginosum 0.04 C

5 Técnicas de ordenación

Me basaré en los scripts que comienzan por to_ de este repo, los cuales explico en los vídeos de “Técnicas de ordenación” de la lista de reproducción “Ecología Numérica con R” de mi canal.

5.1 Ordenación no restringida

5.1.1 PCA aplicado a datos de comunidad transformados

pca_mc_t <- rda(mc_t)
summary(pca_mc_t)

Call:
rda(X = mc_t) 

Partitioning of variance:
              Inertia Proportion
Total          0.5849          1
Unconstrained  0.5849          1

Eigenvalues, and their contribution to the variance 

Importance of components:
                         PC1    PC2     PC3     PC4     PC5     PC6     PC7     PC8     PC9
Eigenvalue            0.1701 0.1357 0.09583 0.05124 0.03892 0.02685 0.02150 0.02036 0.01117
Proportion Explained  0.2908 0.2321 0.16385 0.08761 0.06654 0.04591 0.03675 0.03480 0.01910
Cumulative Proportion 0.2908 0.5229 0.68670 0.77431 0.84086 0.88676 0.92352 0.95832 0.97743
                          PC10     PC11
Eigenvalue            0.007838 0.005365
Proportion Explained  0.013401 0.009173
Cumulative Proportion 0.990827 1.000000

Scaling 2 for species and site scores
* Species are scaled proportional to eigenvalues
* Sites are unscaled: weighted dispersion equal on all dimensions
* General scaling constant of scores:  1.66055 


Species scores

                               PC1      PC2       PC3       PC4      PC5        PC6
Brachymyrmex heeri        0.439707 -0.16503  0.455332 -0.022443 -0.10130  0.0821957
Brachymyrmex sp.         -0.062632  0.07121 -0.107709 -0.057211 -0.15016  0.0536754
Cardiocondyla emeryi     -0.003145  0.03668  0.030314  0.131136  0.07487  0.0824965
Crematogaster steinheili  0.041083 -0.05934 -0.001819  0.142528 -0.02325 -0.1783272
Cyphomyrmex rimosus      -0.151739 -0.12547 -0.044202  0.017088  0.02266 -0.0378973
Dorymyrmex antillana      0.495038  0.08098 -0.415201  0.083178  0.03589  0.0705375
Monomorium floricola      0.028615  0.12512  0.067972  0.174566  0.13108  0.1931665
Monomorium pharaonis      0.029758 -0.05792 -0.013708 -0.156501  0.15945 -0.0007536
Paratrechina longicornis -0.242673 -0.30690 -0.017347 -0.123679  0.14560  0.0769719
Pheidole subarmata       -0.335063 -0.17776 -0.111348 -0.024390 -0.16401  0.1314009
Pseudomyrmex cubaensis   -0.027755 -0.08814 -0.023502  0.020010 -0.02031  0.0836888
Solenopsis geminata      -0.304487  0.58685  0.151790  0.010394  0.04427  0.0139016
Tapinoma melanocephalum   0.029758 -0.05792 -0.013708 -0.156501  0.15945 -0.0007536
Tetramorium caldarium    -0.092937 -0.03537  0.064065 -0.004277 -0.01620  0.0319340
Tetramorium lanuginosum  -0.234492 -0.27163  0.024857  0.306485  0.03777 -0.0181052
Wasmannia auropunctata   -0.062632  0.07121 -0.107709 -0.057211 -0.15016  0.0536754


Site scores (weighted sums of species scores)

           PC1      PC2      PC3      PC4      PC5       PC6
H0098  0.18348 -0.35714 -0.08452 -0.96495  0.98311 -0.004646
H0192 -0.61545  0.91416  0.25602 -0.22598  0.25169 -0.701023
H0333 -0.83682 -0.69194 -0.24377  0.09424  0.12496 -0.208998
H0375  0.17515  0.48769  0.20768  0.23951  0.31003  0.610329
H0488 -0.18746 -0.59531 -0.15874  0.13515 -0.13718  0.565258
H0717  0.31267 -0.26085  0.92786 -0.36190 -0.54163 -0.161516
H0781  0.22657 -0.32723 -0.01003  0.78602 -0.12820 -0.983449
H0796  0.21810  0.45071  0.16531 -0.09902 -0.05607 -0.050833
H0815 -0.38618  0.43910 -0.66411 -0.35275 -0.92585  0.330952
H0867 -0.62773 -0.23890  0.43271 -0.02888 -0.10940  0.215691
H1043  0.58861 -0.09582 -0.01713 -0.09123 -0.24161  0.042321
H1219  0.38168  0.12360 -0.99891  0.07530  0.20607 -0.253613
H1318  0.58861 -0.09582 -0.01713 -0.09123 -0.24161  0.042321
H1337 -0.02124  0.24776  0.20475  0.88573  0.50566  0.557205
screeplot(
  pca_mc_t,
  bstick = TRUE,
  npcs = length(pca_mc_t$CA$eig)
)

# Biplot
cleanplot.pca(pca_mc_t, scaling = 1, mar.percent = 0.06, cex.char1 = 0.7)

5.1.2 Análisis de correspondencia (CA)

# Realizar el CA
mc_ca <- cca(mc)

Resumen de análisis de correspondencia.

summary(mc_ca)

Call:
cca(X = mc) 

Partitioning of scaled Chi-square:
              Inertia Proportion
Total           2.663          1
Unconstrained   2.663          1

Eigenvalues, and their contribution to the scaled Chi-square 

Importance of components:
                         CA1    CA2    CA3    CA4     CA5     CA6     CA7     CA8     CA9    CA10
Eigenvalue            0.5222 0.5027 0.4165 0.3220 0.23134 0.21553 0.17032 0.12871 0.08933 0.06406
Proportion Explained  0.1961 0.1888 0.1564 0.1209 0.08688 0.08094 0.06396 0.04834 0.03355 0.02406
Cumulative Proportion 0.1961 0.3849 0.5413 0.6623 0.74915 0.83009 0.89406 0.94239 0.97594 1.00000

Scaling 2 for species and site scores
* Species are scaled proportional to eigenvalues
* Sites are unscaled: weighted dispersion equal on all dimensions


Species scores

                             CA1      CA2      CA3      CA4      CA5      CA6
Brachymyrmex heeri       -0.3328 -0.45643  0.12858  0.16226  0.28299 -0.15605
Brachymyrmex sp.         -0.3492  2.26883 -1.64556  0.73734 -0.38198  0.06738
Cardiocondyla emeryi     -0.9040 -0.03766  0.92904 -1.03331 -1.23206 -0.10120
Crematogaster steinheili -0.2197 -0.64722  0.99950  2.36939 -0.47188  1.92284
Cyphomyrmex rimosus       2.3585  0.51147  0.73181 -0.35242 -1.62272 -0.11925
Dorymyrmex antillana     -0.4684 -0.21537 -0.14639  0.36809 -0.09386 -0.32860
Monomorium floricola     -1.0482 -0.04207  0.67735 -0.94970 -0.79119 -0.19514
Monomorium pharaonis      0.5049 -1.81566 -2.11709 -0.60454 -0.38666  0.56776
Paratrechina longicornis  1.1097 -0.26080 -0.15699 -0.29469  0.13693 -0.03901
Pheidole subarmata        0.8962  0.76033 -0.03911  0.04078  0.13810 -0.16411
Pseudomyrmex cubaensis    0.8282 -0.07675  0.31327  0.45999  0.79955 -1.59685
Solenopsis geminata      -0.6411  0.62041  0.04964 -0.69630  0.27788  0.43056
Tapinoma melanocephalum   0.5049 -1.81566 -2.11709 -0.60454 -0.38666  0.56776
Tetramorium caldarium     0.7472  0.33775  0.44404 -0.68180  1.75756  0.99228
Tetramorium lanuginosum   0.5621  0.01752  0.68353  0.15237 -0.15391  0.21956
Wasmannia auropunctata   -0.3492  2.26883 -1.64556  0.73734 -0.38198  0.06738


Site scores (weighted averages of species scores)

          CA1      CA2      CA3     CA4     CA5      CA6
H0098  0.5049 -1.81566 -2.11709 -0.6045 -0.3867  0.56776
H0192 -1.2278  1.23409  0.11917 -2.1622  1.2012  1.99763
H0333  2.3585  0.51147  0.73181 -0.3524 -1.6227 -0.11925
H0375 -1.1923 -0.04647  0.42566 -0.8661 -0.3503 -0.28909
H0488  0.8282 -0.07675  0.31327  0.4600  0.7995 -1.59685
H0717 -0.6373 -0.90790  0.30872  0.5039  1.2233 -0.72403
H0781 -0.2197 -0.64722  0.99950  2.3694 -0.4719  1.92284
H0796 -0.9207 -0.03407  0.02547 -0.1718  0.6729 -0.08366
H0815 -0.3492  2.26883 -1.64556  0.7373 -0.3820  0.06738
H0867  0.7472  0.33775  0.44404 -0.6818  1.7576  0.99228
H1043 -0.7672 -0.66815 -0.02138  0.8234  0.4088 -1.12430
H1219 -0.8970 -0.42840 -0.35148  1.1430 -0.4057 -1.52457
H1318 -0.7672 -0.66815 -0.02138  0.8234  0.4088 -1.12430
H1337 -0.9040 -0.03766  0.92904 -1.0333 -1.2321 -0.10120

Gráfico de sedimentación o screeplot.

# Screeplot
screeplot(mc_ca, bstick = TRUE, npcs = length(mc_ca$CA$eig))

Representación del biplot.

# Biplot
plot(mc_ca,
     scaling = 1,
     main = "Análisis de correspondencia, escalamiento 1"
)

5.2 Ordenación restringida con modelización

A continuación, el análisis de ordenación propiamente. La parte más importante es el entrenamiento: la función train del paquete caret, contenida en la función my_train, simplifica la selección de variables. Lo más importante: prueba con todas las variables primero, observa las variables que recomienda el modelo final (print_my_train(mod)) y ensaya varias combinaciones de subconjuntos de variables.

mc_t_ren <- mc_t %>%
  rename_all(~ paste('ESPECIE', .x))
env_spp <- env %>% bind_cols(mc_t_ren)
spp <- paste0('`', grep('^ESPECIE', colnames(env_spp), value = T), '`', collapse = ' + ')
my_formula <- as.formula(paste(spp, '~ .'))
set.seed(1); mod <- my_train(
  formula = my_formula, 
  # preproceso = 'scale',
  data = env_spp,
  num_variables = 2)
print_my_train(mod)
$resumen_variables
Subset selection object
4 Variables  (and intercept)
          Forced in Forced out
porc_SUEL     FALSE      FALSE
porc_DOSE     FALSE      FALSE
porc_CONS     FALSE      FALSE
porc_EDIF     FALSE      FALSE
1 subsets of each size up to 2
Selection Algorithm: 'sequential replacement'
         porc_SUEL porc_DOSE porc_CONS porc_EDIF
1  ( 1 ) " "       "*"       " "       " "      
2  ( 1 ) "*"       "*"       " "       " "      

$resultados_nvmax
  nvmax      RMSE   Rsquared       MAE   RMSESD RsquaredSD     MAESD
1     2 0.7491609 0.08211278 0.5789065 0.209041 0.03534104 0.1702864

$mejor_ajuste
  nvmax
1     2
(covar <- grep(
  pattern = '\\(Intercept\\)',
  x = names(coef(mod$finalModel,unlist(mod$bestTune))),
  invert = T, value = T))
[1] "porc_SUEL" "porc_DOSE"
rda_mc_t <- rda(mc_t_ren %>% rename_all(~ gsub('^ESPECIE ', '', .)) ~ .,
                    env %>% select_at(all_of(gsub('\\`', '', covar))), scale = T)

A continuación, el resumen del análisis de redundancia.

summary(rda_mc_t)

Call:
rda(formula = mc_t_ren %>% rename_all(~gsub("^ESPECIE ", "",      .)) ~ porc_SUEL + porc_DOSE, data = env %>% select_at(all_of(gsub("\\`",      "", covar))), scale = T) 

Partitioning of correlations:
              Inertia Proportion
Total          16.000     1.0000
Constrained     3.981     0.2488
Unconstrained  12.019     0.7512

Eigenvalues, and their contribution to the correlations 

Importance of components:
                        RDA1    RDA2    PC1    PC2    PC3     PC4     PC5     PC6    PC7     PC8
Eigenvalue            2.8977 1.08366 2.8146 2.3812 1.6109 1.21299 1.17354 1.06584 0.6688 0.46179
Proportion Explained  0.1811 0.06773 0.1759 0.1488 0.1007 0.07581 0.07335 0.06661 0.0418 0.02886
Cumulative Proportion 0.1811 0.24883 0.4247 0.5736 0.6743 0.75007 0.82341 0.89003 0.9318 0.96069
                          PC9   PC10     PC11
Eigenvalue            0.38509 0.2223 0.021538
Proportion Explained  0.02407 0.0139 0.001346
Cumulative Proportion 0.98476 0.9987 1.000000

Accumulated constrained eigenvalues
Importance of components:
                        RDA1   RDA2
Eigenvalue            2.8977 1.0837
Proportion Explained  0.7278 0.2722
Cumulative Proportion 0.7278 1.0000

Scaling 2 for species and site scores
* Species are scaled proportional to eigenvalues
* Sites are unscaled: weighted dispersion equal on all dimensions
* General scaling constant of scores:  3.797658 


Species scores

                              RDA1     RDA2      PC1        PC2      PC3      PC4
Brachymyrmex heeri        0.414803  0.48619  0.19138  0.2352238 -0.01822  0.11671
Brachymyrmex sp.         -0.250396 -0.17834 -0.56967 -0.6177494 -0.12549  0.10002
Cardiocondyla emeryi     -0.083746 -0.31389 -0.14041  0.5933415 -0.19714 -0.00281
Crematogaster steinheili -0.156856  0.20127 -0.01017  0.2070065 -0.53036  0.55958
Cyphomyrmex rimosus      -0.520659  0.04775  0.22011 -0.0008510  0.10077 -0.21457
Dorymyrmex antillana      0.420519  0.11183 -0.14945 -0.1115097 -0.57904 -0.18275
Monomorium floricola      0.139044 -0.26140 -0.20788  0.5611355 -0.04633 -0.09042
Monomorium pharaonis      0.193793 -0.31902  0.68393 -0.3542808 -0.15164  0.22376
Paratrechina longicornis -0.555860 -0.04327  0.69398 -0.1742578  0.19328 -0.13328
Pheidole subarmata       -0.811397  0.03766 -0.02720 -0.3258284  0.20832 -0.20447
Pseudomyrmex cubaensis   -0.323714  0.07168  0.18648  0.0006707 -0.17921 -0.58675
Solenopsis geminata       0.004058 -0.46301 -0.45755  0.1397867  0.47143  0.15489
Tapinoma melanocephalum   0.193793 -0.31902  0.68393 -0.3542808 -0.15164  0.22376
Tetramorium caldarium    -0.309350  0.13692  0.11618  0.0566512  0.58994  0.35064
Tetramorium lanuginosum  -0.756721  0.09770  0.20519  0.4418959 -0.15293  0.08985
Wasmannia auropunctata   -0.250396 -0.17834 -0.56967 -0.6177494 -0.12549  0.10002


Site scores (weighted sums of species scores)

         RDA1    RDA2      PC1       PC2      PC3      PC4
H0098  0.7960 -1.8312  2.63619 -1.365574 -0.58450  0.86249
H0192  0.2412 -1.6653 -0.51748  0.262127  1.41353  0.11004
H0333 -2.4189  0.2160  0.84843 -0.003280  0.38843 -0.82707
H0375  0.9163 -0.7378 -0.55280  0.817644  0.39878 -0.42381
H0488 -1.3642  1.0775  0.71878  0.002585 -0.69077 -2.26161
H0717  0.7501  1.4328  0.07702 -0.007102  0.77634  0.14805
H0781 -0.1209  1.8080 -0.03921  0.797906 -2.04429  2.15692
H0796  0.8362  0.1069 -0.49918  0.023110  0.26200  0.16240
H0815 -0.9148 -1.9605 -2.19579 -2.381113 -0.48371  0.38551
H0867 -1.5541  0.5689  0.44782  0.218362  2.27391  1.35156
H1043  0.9676  1.2229 -0.11720 -0.274125 -0.01514 -0.39939
H1219  0.7568  0.1928 -0.27810 -0.399241 -0.58107 -0.80819
H1318  0.9676  1.2229  0.01274  0.021668 -0.35362 -0.44606
H1337  0.1412 -1.6542 -0.54122  2.287033 -0.75989 -0.01083


Site constraints (linear combinations of constraining variables)

           RDA1    RDA2      PC1       PC2      PC3      PC4
H0098  0.746974 -1.2297  2.63619 -1.365574 -0.58450  0.86249
H0192  0.608205 -1.9672 -0.51748  0.262127  1.41353  0.11004
H0333 -2.006880  0.1840  0.84843 -0.003280  0.38843 -0.82707
H0375  0.928883 -0.2629 -0.55280  0.817644  0.39878 -0.42381
H0488 -1.247754  0.2763  0.71878  0.002585 -0.69077 -2.26161
H0717  0.983950  1.4461  0.07702 -0.007102  0.77634  0.14805
H0781 -0.604601  0.7758 -0.03921  0.797906 -2.04429  2.15692
H0796 -0.006582  0.9652 -0.49918  0.023110  0.26200  0.16240
H0815 -0.965149 -0.6874 -2.19579 -2.381113 -0.48371  0.38551
H0867 -1.192388  0.5277  0.44782  0.218362  2.27391  1.35156
H1043  1.273455  1.5683 -0.11720 -0.274125 -0.01514 -0.39939
H1219  0.955551  0.3004 -0.27810 -0.399241 -0.58107 -0.80819
H1318  0.849135 -0.6868  0.01274  0.021668 -0.35362 -0.44606
H1337 -0.322799 -1.2099 -0.54122  2.287033 -0.75989 -0.01083


Biplot scores for constraining variables

             RDA1    RDA2 PC1 PC2 PC3 PC4
porc_SUEL  0.3888 -0.9213   0   0   0   0
porc_DOSE -0.9828  0.1849   0   0   0   0

La varianza ajustada explicada por el modelo.

RsquareAdj(rda_mc_t)$adj.r.squared
[1] 0.1122566

Y el factor de inflación de la varianza.

vif.cca(rda_mc_t)
porc_SUEL porc_DOSE 
 1.439277  1.439277 

Represento el gráfico triplot.

# Triplot
escalado <- 1
plot(rda_mc_t,
     scaling = escalado,
     display = c("sp", "lc", "cn"),
     main = paste("Triplot de RDA especies ~ variables, escalamiento", escalado)
)
rda_mc_t_sc1 <- scores(rda_mc_t,
         choices = 1:2,
         scaling = escalado,
         display = "sp"
  )
# text(mi_fam_t_rda, "species", col="red", cex=0.8, scaling=escalado)
arrows(0, 0,
       rda_mc_t_sc1[, 1] * 0.9,
       rda_mc_t_sc1[, 2] * 0.9,
       length = 0,
       lty = 1,
       col = "red"
)

6 Análisis de diversidad + análisis de agrupamiento abreviado

Me basaré en los scripts que comienzan por di_ de este repo, los cuales explico en los vídeos de “Análisis de diversidad” (vídeos 19 y 20) de la lista de reproducción “Ecología Numérica con R” de mi canal. Dichos vídeos tienen aplicaciones ligeramente diferentes, pues los datos fuente usados en ellos son de abundancia, mientras que los tuyos son de presencia/ausencia.

6.1 Calcular riqueza (e índices)

La principal desventaja de trabajar con registros de presencia, es que la mayoría de los índices de diversidad alpha fueron diseñados originalmente para calcularse a partir de datos de abundancia. Sin embargo, la riqueza de especies, que es el número \(q=0\) de Hill (\(=N_0\) en las columnas que produce la función alpha_div) es un buen proxy sobre la diversidad, y nos ayudará a comparar sitios.

Además de la columna N0 del objeto que generaré en el bloque siguiente, verás que la función alpha_div genera otras columnas; son índices pensados para datos de abundancia, que en este caso no usaremos, pero los muestro para que tengas una visión completa del análisis de diversidad con índices que podría serte de utilidad en el futuro.

Por otra parte, afortunadamente, los métodos de estimación de riqueza de Chao, y los de diversidad beta (al final de esta sección), aprovechan sustancialmente los registros de presencia/ausencia para realizar estimaciones consistentes y fiables.

Una nota adicional. En el análisis de diversidad, es útil (no imprescindible) disponer de un análisis clúster (agrupamiento) básico. Este te servirá para comparar la riqueza observada y la esperada entre hábitats. Por esta razón, combinamos análisis de diversidad con agrupamiento. Sin embargo, si el análisis de agrupamiento generó grupos de dos o menos elementos, dicha comparación no será realizable.

indices <- alpha_div(mc) %>% 
  mutate(sitio = rownames(.)) %>% 
  relocate(sitio, .before = everything())

El objeto mc es la matriz de comunidad de presecia/ausencia. La función alpha_div es un “envoltorio” generado por mí para calcular múltiples índices de diversidad y estimaciones, basada en las funciones de los paquetes SpadeR y iNEXT. Si usásemos datos de abundancia, los índices que calcula la función “alpha_div” serían útiles, pero con registros de presencia/ausencia, como es nuestro caso, sólo la columna N0 (riqueza) nos aportará algún resultado con sentido.

indices %>% 
  kable(booktabs=T) %>%
  kable_styling(latex_options = c("HOLD_position", "scale_down")) %>%
  gsub(' NA |NaN ', '', .) #Lista de especies
sitio N0 H Hb2 N1 N1b2 N2 J E10 E20
H0098 H0098 5 1.6094379 2.321928 5 5 5 1 1 1
H0192 H0192 1 0.0000000 0.000000 1 1 1 1 1
H0333 H0333 4 1.3862944 2.000000 4 4 4 1 1 1
H0375 H0375 4 1.3862944 2.000000 4 4 4 1 1 1
H0488 H0488 6 1.7917595 2.584963 6 6 6 1 1 1
H0717 H0717 1 0.0000000 0.000000 1 1 1 1 1
H0781 H0781 4 1.3862944 2.000000 4 4 4 1 1 1
H0796 H0796 3 1.0986123 1.584963 3 3 3 1 1 1
H0815 H0815 5 1.6094379 2.321928 5 5 5 1 1 1
H0867 H0867 6 1.7917595 2.584963 6 6 6 1 1 1
H1043 H1043 2 0.6931472 1.000000 2 2 2 1 1 1
H1219 H1219 1 0.0000000 0.000000 1 1 1 1 1
H1318 H1318 2 0.6931472 1.000000 2 2 2 1 1 1
H1337 H1337 6 1.7917595 2.584963 6 6 6 1 1 1

Los sitios ordenados en función de su riqueza:

indices %>%
  arrange(desc(N0)) %>% 
  kable(booktabs=T) %>%
  kable_styling(latex_options = c("HOLD_position", "scale_down")) %>%
  gsub(' NA |NaN ', '', .) #Lista de especies
sitio N0 H Hb2 N1 N1b2 N2 J E10 E20
H0488 H0488 6 1.7917595 2.584963 6 6 6 1 1 1
H0867 H0867 6 1.7917595 2.584963 6 6 6 1 1 1
H1337 H1337 6 1.7917595 2.584963 6 6 6 1 1 1
H0098 H0098 5 1.6094379 2.321928 5 5 5 1 1 1
H0815 H0815 5 1.6094379 2.321928 5 5 5 1 1 1
H0333 H0333 4 1.3862944 2.000000 4 4 4 1 1 1
H0375 H0375 4 1.3862944 2.000000 4 4 4 1 1 1
H0781 H0781 4 1.3862944 2.000000 4 4 4 1 1 1
H0796 H0796 3 1.0986123 1.584963 3 3 3 1 1 1
H1043 H1043 2 0.6931472 1.000000 2 2 2 1 1 1
H1318 H1318 2 0.6931472 1.000000 2 2 2 1 1 1
H0192 H0192 1 0.0000000 0.000000 1 1 1 1 1
H0717 H0717 1 0.0000000 0.000000 1 1 1 1 1
H1219 H1219 1 0.0000000 0.000000 1 1 1 1 1

6.2 Evaluar correlación entre riqueza y variables ambientales mediante matriz de correlación.

En el bloque siguiente, represento gráficamente la correlación entre la riqueza y las variables ambientales mediante un panel de gráficos, que suele llamarse también “matriz de correlación”, expresada gráficamente. Si usases índices de diversidad, como el de Shannon o los números de Hill, también deberías incluirlos en el gráfico; nota que en este ejemplo, sólo uso la riqueza (la función select(N0) se encarga de conservar sólo la riqueza). Esto es lo que debes saber sobre el panel:

  • Presta atención a la primera columna y la primera fila de la matriz, que muestra cómo se correlaciona N0 con las variables ambientales que elijas.

  • La diagonal contiene gráficos de línea que muestra la densidad de la variable en cuestión.

  • Los gráficos del “triángulo superior”, y que contienen el patrón Corr: ####, muestran el valor del coeficiente de correlación de Pearson (\(r\)) entre las variables intersectadas. Si existe un \(|r|\) elevado (es decir, si es muy cercano a -1 o a 1) y la prueba de producto-momento es significativa (si hay uno o varios asteriscos, o un punto, lo es), entonces toma nota de que dicha variable se asocia estadísticamente con la riqueza. Si \(r\) es negativo, la relación es inversa (cuando aumenta la variable, disminuye la riqueza, y viceversa); si es positivo, la relación es directa (cuando aumenta la variable, aumenta también la riqueza).

  • En el “triángulo inferior”, que es un espejo del superior, se sitúan los gráficos de dispersión de las variables intersectadas. Si los puntos siguen un patrón de distribución formando una elipse imaginaria (organizados en torno a una línea recta imaginaria inclinada), entonces existe correlación.

bind_cols(indices %>% select(N0), env %>%
            rename_with(.fn = ~ paste0('AMB_', .))) %>%
  ggpairs(
    labeller = label_wrap_gen(width=10),
    upper = list(continuous = wrap("cor", size = 3))) +
  theme(text = element_text(size = 10))

6.3 “Completitud de muestra” y curva de acumulación

“Completitud”, en porcentajes, según distintos estimadores. Con un 80% de completitud, se considera en general una muestra representativa. Sin embargo, este umbral de 80% no debe tomarse de forma estricta. Sobre todo porque existen métodos refinados que mejoran las estimaciones

riqueza_estimaciones <- data.frame(specpool(mc) %>% select(-matches('.se$'))) %>% 
  select(`Riqueza observada` = Species,
         `Número de sitios` = n,
         `Estimación por Chao (clásico)` = chao,
         `Estimación por jackknife de primer orden` = jack1,
         `Estimación por jackknife de segundo orden` = jack2,
         `Estimación por bootstrap` = boot) %>% 
  pivot_longer(cols = everything(), names_to = 'Variable', values_to = 'Valor') %>%
  mutate(`Cobertura (%)` = Valor / (filter(., Variable == "Riqueza observada") %>% pull(Valor)) * 100) %>% 
  mutate(`Cobertura (%)` = ifelse(Variable %in% c('Riqueza observada', 'Número de sitios'), NA, `Cobertura (%)`))
riqueza_estimaciones %>% estilo_kable(alinear = 'lrr')
TABLA 6.1:
Variable Valor Cobertura (%)
Riqueza observada 16.00
Número de sitios 14.00
Estimación por Chao (clásico) 53.61 335.04
Estimación por jackknife de primer orden 24.36 152.23
Estimación por jackknife de segundo orden 31.28 195.50
Estimación por bootstrap 19.33 120.78
# POSIBLE ERROR DE BUG NO RESUELTO. PODRÍA APARECER EL SIGUIENTE ERROR:
# Error in if (var_mle > 0) { : valor ausente donde TRUE/FALSE es necesario
# Varios intentos frustrados por lograr que funcione. Entiendo que el problema
# está en el número de doubletons (la matriz no tiene), pero no logré mejorar
# la función interna SpecInciHomo para solucionarlo. La versión de SpadeR usada
# en la aplicación Shiny https://chao.shinyapps.io/SpadeR/, no es la misma que 
# la que se encuentra en GitHub ni en el CRAN, pues esa no tiene bug.
df_spader <- data.frame(V1 = as.integer(c(nrow(mc), colSums(mc))))
df_spader %>% estilo_kable(
  titulo = 'Matriz de datos tal como la requiere el paquete SpadeR',
  nombres_filas = T, alinear = 'r', cubre_anchura = T)
TABLA 6.2: Matriz de datos tal como la requiere el paquete SpadeR
V1
1 14
2 10
3 1
4 1
5 1
6 1
7 10
8 2
9 1
10 4
11 4
12 1
13 6
14 1
15 1
16 5
17 1
tryCatch(
  expr = ChaoSpecies(df_spader, datatype = 'incidence_freq',
            k = min(df_spader$V1), conf=0.95),
  error = function(cond) {
      message("Se detectó un error", appendLF = TRUE)
      message('Esta información podría ayudar a depurar: ', cond, appendLF = TRUE)
      message('\nSaliendo...')},
    warning = function(warn) {
      message("Hubo una advertencia", appendLF = TRUE)
      message('Mostrando la advertencia a continuación: ', warn, appendLF = TRUE)},
    finally = {
      message('Estimación de riqueza generada satisfactoriamente')})
## 
## (1) BASIC DATA INFORMATION:
## 
##                                          Variable Value
##     Number of observed species                  D    16
##     Number of sampling units                    T    14
##     Total number of incidences                  U    50
##     Coverage estimate for entire dataset        C 0.823
##     CV for entire dataset                      CV  1.07
## 
##                                                       Variable Value
##     Cut-off point                                            k     1
##     Total number of incidences in infrequent group    U_infreq     9
##     Number of observed species for infrequent group   D_infreq     9
##     Estimated sample coverage for infrequent group    C_infreq 0.017
##     Estimated CV for infrequent group in ICE         CV_infreq     0
##     Estimated CV1 for infrequent group in ICE-1     CV1_infreq     0
##     Number of observed species for frequent group       D_freq     7
## 
##                            Q1
##     Incidence freq. counts  9
## 
## 
## (2) SPECIES RICHNESS ESTIMATORS TABLE:
## 
##                               Estimate     s.e. 95%Lower  95%Upper
##     Homogeneous Model          542.500 7431.915   21.779 47982.063
##     Chao2 (Chao, 1987)          53.607   45.612   21.830   258.588
##     Chao2-bc                    32.714   15.057   19.689    91.723
##     iChao2 (Chiu et al. 2014)   53.607   42.490   22.357   238.493
##     ICE (Lee & Chao, 1994)     542.500 7431.915   21.779 47982.063
##     ICE-1 (Lee & Chao, 1994)   542.500 7431.915   21.779 47982.063
##     1st order jackknife         24.357    4.015   19.421    36.414
##     2nd order jackknife         31.280    6.679   22.733    50.677
## 
## 
## (3) DESCRIPTION OF ESTIMATORS/MODELS:
## 
## Homogeneous Model: This model assumes that all species have the same incidence or detection probabilities. See Eq. (3.2) of Lee and Chao (1994) or Eq. (12a) in Chao and Chiu (2016b).
## 
## Chao2 (Chao, 1987): This approach uses the frequencies of uniques and duplicates to estimate the number of undetected species; see Chao (1987) or Eq. (11a) in Chao and Chiu (2016b).
##      
## Chao2-bc: A bias-corrected form for the Chao2 estimator; see Chao (2005).
##   
## iChao2: An improved Chao2 estimator; see Chiu et al. (2014).
## 
## ICE (Incidence-based Coverage Estimator): A non-parametric estimator originally proposed by Lee and Chao (1994) in the context of capture-recapture data analysis. The observed species are separated as frequent and infrequent species groups; only data in the infrequent group are used to estimate the number of undetected species. The estimated CV for species in the infrequent group characterizes the degree of heterogeneity among species incidence probabilities. See Eq. (12b) of Chao and Chiu (2016b), which is an improved version of Eq. (3.18) in Lee and Chao (1994). This model is also called Model(h) in capture-recapture literature where h denotes "heterogeneity".
## 
## ICE-1: A modified ICE for highly-heterogeneous cases.
## 
## 1st order jackknife: It uses the frequency of uniques to estimate the number of undetected species; see Burnham and Overton (1978).
## 
## 2nd order jackknife: It uses the frequencies of uniques and duplicates to estimate the number of undetected species; see Burnham and Overton (1978).
## 
## 95% Confidence interval: A log-transformation is used for all estimators so that the lower bound of the resulting interval is at least the number of observed species. See Chao (1987).
# Si apareciera ...
# "Error in if (var_mle > 0) { : valor ausente donde TRUE/FALSE es necesario
# ... entonces usar tabla básica de estimación: "riqueza_estimaciones"

Graficaré la curva de acumulación de especies.

mc_general <- mc %>%
  summarise_all(sum) %>%
  mutate(N = nrow(mc)) %>%
  relocate(N, .before = 1) %>%
  data.frame
nasin_raref <- iNEXT::iNEXT(
  x = t(mc_general),
  q=0,
  knots = 2000,
  datatype = 'incidence_freq')
acumulacion_especies <- iNEXT::ggiNEXT(nasin_raref, type=1) +
  theme_bw() +
  theme(
    text = element_text(size = 20),
    panel.background = element_rect(fill = 'white', colour = 'black'),
    panel.grid.major = element_line(colour = "grey", linetype = "dashed", size = 0.25)
  ) +
  ylab('Riqueza de especies') +
  xlab('Número de sitios') +
  scale_y_continuous(breaks = seq(0, 80, length.out = 9)) +
  scale_color_manual(values = brewer.pal(8, 'Set2')) +
  scale_fill_manual(values = brewer.pal(8, 'Set2'))
acumulacion_especies

Ahora según los grupos previamente seleccionados en el análisis de agrupamiento.

grupos_seleccionados <- readRDS(paste0(
  fuentes_manuscrito, 'grupos_seleccionados-',
  params$estudiante, '.RDS'))
mc_grupos <- mc %>%
  mutate(g = grupos_seleccionados) %>%
  group_by(g) %>%
  summarise_all(sum) %>%
  select(-g) %>% 
  mutate(N = nrow(mc)) %>% 
  relocate(N, .before = 1) %>% 
  data.frame
nasin_raref_general <- iNEXT::iNEXT(
  x = t(mc_grupos),
  q=0,
  knots = 400,
  datatype = 'incidence_freq')
acumulacion_especies_grupos <- iNEXT::ggiNEXT(nasin_raref_general, type=1) +
  theme_bw() +
  theme(
    text = element_text(size = 20),
    panel.background = element_rect(fill = 'white', colour = 'black'),
    panel.grid.major = element_line(colour = "grey", linetype = "dashed", size = 0.25)
  ) +
  ylab('Riqueza de especies') +
  xlab('Número de sitios') +
  scale_y_continuous(breaks = seq(0, 80, length.out = 9)) +
  scale_color_manual(values = brewer.pal(8, 'Set2')) +
  scale_fill_manual(values = brewer.pal(8, 'Set2'))
acumulacion_especies_grupos

6.4 Contribución de especies a la diversidad beta (SCBD, species contribution to beta diversity) y contribución local a la diversidad beta (LCBD local contribution to beta diversity)

determinar_contrib_local_y_especie(
    mc = mc,
    alpha = 0.05,
    nperm = 9999,
    metodo = 'sorensen')
## $betadiv
## $beta
##   SStotal   BDtotal 
## 4.0324727 0.3101902 
## 
## $SCBD
## [1] NA
## 
## $LCBD
##      H0098      H0192      H0333      H0375      H0488      H0717      H0781      H0796      H0815 
## 0.07216708 0.12687652 0.12174527 0.04584501 0.05802708 0.09111244 0.05749926 0.03336132 0.08944590 
##      H0867      H1043      H1219      H1318      H1337 
## 0.07364703 0.04240074 0.09026894 0.04240074 0.05520266 
## 
## $p.LCBD
##  H0098  H0192  H0333  H0375  H0488  H0717  H0781  H0796  H0815  H0867  H1043  H1219  H1318  H1337 
## 0.4801 0.0441 0.0489 0.8336 0.6367 0.1917 0.6366 0.9942 0.2142 0.4239 0.8858 0.2114 0.8863 0.6784 
## 
## $p.adj
##  H0098  H0192  H0333  H0375  H0488  H0717  H0781  H0796  H0815  H0867  H1043  H1219  H1318  H1337 
## 1.0000 0.6174 0.6357 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 
## 
## $method
## [1] "sorensen"     "sqrt.D=FALSE"
## 
## $note
## [1] "Info -- D is Euclidean because beta.div outputs D[jk] = sqrt(1-S[jk])"
## [2] "For this D functions, use beta.div with option sqrt.D=FALSE"          
## 
## $D
## [1] NA
## 
## attr(,"class")
## [1] "beta.div"
## 
## $especies_contribuyen_betadiv
## [1] NA
## 
## $sitios_contribuyen_betadiv
## [1] "H0192" "H0333"
## 
## $valor_de_ajustado_lcbd
##  H0098  H0192  H0333  H0375  H0488  H0717  H0781  H0796  H0815  H0867  H1043  H1219  H1318  H1337 
## 1.0000 0.6174 0.6357 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 
## 
## $sitios_contribuyen_betadiv_ajustado
## character(0)

Referencias

Borcard, Daniel, François Gillet, y Pierre Legendre. 2018. Numerical ecology with R. Springer.
Martínez Batlle, José Ramón. 2020. biogeografia-master/scripts-de-analisis-BCI: Long coding sessions (versión v0.0.0.9000). Zenodo. https://doi.org/10.5281/zenodo.4402362.